package com.weixin.project.Service;

import com.weixin.project.Common.Enum.LikedStatus;
import com.weixin.project.Common.Enum.ModuleType;
import com.weixin.project.Dao.CommentDao;
import com.weixin.project.Dao.UserLikeDao;
import com.weixin.project.Entity.Comment;
import com.weixin.project.Entity.UserLike;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Optional;
import java.util.Set;

@Service
public class CommonService {
    @Autowired
    RedisTemplate redisTemplate;
    @Autowired
    QuestionService questionService;
    @Autowired
    CommentDao commentDao;
    @Autowired
    UserLikeDao userLikeDao;
    private static final String LIKED_STATUS_HASH = "liked_status_hash";
    private static final String LIKED_COUNT_HASH = "liked_count_hash";
    private static final String COMMENT = "comment";
    private static final String COMMENT_LIST_OF_QUESTION = "comment_list_of_question";
    @CachePut(value = COMMENT, key = "#result.id", unless = "#result == null ")
    public Comment storeComment(Comment comment){
        redisTemplate.delete(COMMENT_LIST_OF_QUESTION + "::" + comment.getModule().getValue() + "_" + comment.getQuestionId());
        return commentDao.save(comment);
    }

    @Cacheable(value = COMMENT_LIST_OF_QUESTION, key = "#moduleType.value + '_' + #questionId", unless = "#result == null ")
    public List<Comment> findComments(Integer questionId, ModuleType moduleType){
        return commentDao.getCommentsByQuestionIdAndModule(questionId, moduleType);
    }

    @Cacheable(value = COMMENT, key = "#commentId", unless = "#result == null ")
    public Comment findComment(Integer commentId){
        Optional<Comment> commentOptional = commentDao.findById(commentId);
        return commentOptional.isPresent()?commentOptional.get():null;
    }

    /**
     * 包含用户点赞/取消点赞某一评论所进行的操作
     * @param comment 评论
     * @param userId 用户id
     */
    public void likeOneComment(Comment comment, Integer userId){
        //获取用户对该评论的点赞状态
        String user_comment_key = "key::" + userId + "::" + comment.getId();
        LikedStatus curLikedStatus = ifUserHasLiked(comment.getId(),userId);

        //点赞->取消 未点赞/取消->点赞
        redisTemplate.boundHashOps(LIKED_STATUS_HASH).put(user_comment_key, LikedStatus.reverse(curLikedStatus).getValue());

        //获取评论当前的点赞数量
        String comment_id_key = "key::" + comment.getId().toString();
        Integer curLikedCount = countLikesOfComment(comment);

        // 当前状态为已点赞，则操作为取消点赞，数量 -1，反之同理
        // TODO: 此处面对并发点赞时可能会出错
        Integer newLikedCount = curLikedStatus.equals(LikedStatus.LIKED)?
                curLikedCount - 1 : curLikedCount + 1;

        redisTemplate.boundHashOps(LIKED_COUNT_HASH).put(comment_id_key, newLikedCount);
    }

    public LikedStatus ifUserHasLiked(Integer commentId, Integer userId){
        LikedStatus likedStatus;
        String user_comment_key = "key::" + userId + "::" + commentId;
        if (redisTemplate.boundHashOps(LIKED_STATUS_HASH).hasKey(user_comment_key)) {
            likedStatus = LikedStatus.codeOf(Integer.parseInt(
                    redisTemplate.boundHashOps(LIKED_STATUS_HASH).get(user_comment_key).toString()));
        }
        else {
            UserLike userLike = userLikeDao.findUserLikeByUserIdAndCommentId(userId, commentId);
            if (userLike == null) likedStatus = LikedStatus.UNLIKED;
            else likedStatus = userLike.getStatus();
        }
        redisTemplate.boundHashOps(LIKED_STATUS_HASH).put(user_comment_key, likedStatus.getValue());
        return likedStatus;
    }

    public Integer countLikesOfComment(Comment comment){
        String comment_id_key = "key::" + comment.getId().toString();
        Integer likedCount = 0;
        if (redisTemplate.boundHashOps(LIKED_COUNT_HASH).hasKey(comment_id_key)){
            likedCount = Integer.parseInt(redisTemplate.boundHashOps(LIKED_COUNT_HASH).get(comment_id_key).toString());
        }
        else {
            //只有redis中没有key的情况下，comment的good属性才是实际的点赞数量，否则以redis中的点赞数量为准
            likedCount = comment.getGood();
        }
        redisTemplate.boundHashOps(LIKED_COUNT_HASH).put(comment_id_key, likedCount);
        return likedCount;
    }

    /**
     * 将redis数据库中的点赞状态，点赞数据持久化到数据库中
     */
    @Transactional
    public void transLikeFromRedis2DB(){
        Set<String> keys = redisTemplate.boundHashOps(LIKED_STATUS_HASH).keys();
        for (String key :
                keys) {
            String[] split = key.split("::");
            Integer userId = Integer.parseInt(split[1]);
            Integer commentId = Integer.parseInt(split[2]);
            UserLike userLike = userLikeDao.findUserLikeByUserIdAndCommentId(userId, commentId);
            if (userLike == null){
                userLike = new UserLike();
                userLike.setUserId(userId);
                userLike.setCommentId(commentId);
                Comment comment = findComment(commentId);
                userLike.setModule(comment.getModule());
                userLike.setQuestionId(comment.getModule().equals(ModuleType.QUESTION)?
                        questionService.findQuestionById(comment.getQuestionId()).getId():-1);
            }
            userLike.setStatus(LikedStatus.codeOf(Integer.parseInt(
                    redisTemplate.boundHashOps(LIKED_STATUS_HASH).get(key).toString())));
            userLikeDao.save(userLike);
            redisTemplate.boundHashOps(LIKED_STATUS_HASH).delete(key);
        }

        keys = redisTemplate.boundHashOps(LIKED_COUNT_HASH).keys();
        for (String key:
                keys) {
            String[] split = key.split("::");
            Integer commentId = Integer.parseInt(split[1]);
            Comment comment = findComment(commentId);
            comment.setGood(Integer.parseInt(
                    redisTemplate.boundHashOps(LIKED_COUNT_HASH).get(key).toString()));
            storeComment(comment);
            redisTemplate.boundHashOps(LIKED_COUNT_HASH).delete(key);
        }
    }
}
